﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Reactive;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using ZOIL.DataModel;
using ZOIL.Framework.Presentation;
using ZOIL.Library.Behaviors;
using ZOIL.Framework.Core;

namespace ZOIL.Library.Components
{
    [ViewModelFor(typeof(ZPortalShapeView))]
    public class ZPortalShapeViewModel : ConnectionBaseViewModel
    {
        #region properties

        #region Model

        public new ZPortalShape Model
        {
            get
            {
                return base.Model as ZPortalShape;
            }
            set
            {
                base.Model = value;
            }
        }

        #endregion

        #endregion

        #region ctor

        public ZPortalShapeViewModel()
        {
            ConnectionShape = new RectangleGeometry();

            _shapeMoves = new Subject<Unit>();
            _shapeMoveSubscription = _shapeMoves
                //.Sample(TimeSpan.FromMilliseconds(50))
                .ObserveOnDispatcher()
                .Subscribe(_ => OnShapeMoved());
        }

        #endregion

        #region private fields

        private ZInformationLandscape _landscape;

        private Geometry _shapeGeometry;

        private IDisposable _shapeMoveSubscription;
        private IDisposable _shapePropertyChanged;

        private readonly Subject<Unit> _shapeMoves;

        #endregion

        #region private methods

        private void InitShape()
        {
            _landscape = ZTreeHelper.TryFindParent<ZInformationLandscape>(View);

            var rootCollection = DatabaseCollection.Collection;

            var shapeProp = rootCollection.GetVisualProperties(Model.DatabaseId);

            if (!Model.IsInitialized && !Model.WasRemotelyCreated)
            {
                shapeProp.Width = Model.Bounds.Width;
                shapeProp.Height = Model.Bounds.Height;
                shapeProp.X = Model.Bounds.Left;
                shapeProp.Y = Model.Bounds.Top;
                //shapeProp.TemplateId = "PortalShapeTemplate";
                Model.IsInitialized = true;
            }

            //bei Änderung schauen was drunter liegt
            _shapePropertyChanged = Observable.FromEventPattern(shapeProp, "PropertyChanged")
                      .ObserveOnDispatcher()
                      .Subscribe(_ => OnShapeMoved());

            OnShapeMoved();
        }

        private void RecalculateShapeGeometry()
        {
            // Keep geometry locally to avoid jumping connection
            _shapeGeometry = Model.ShapeGeometry;

            var offsetX = Canvas.GetLeft(View) - Model.Bounds.Left;
            var offsetY = Canvas.GetTop(View) - Model.Bounds.Top;

            if (_shapeGeometry.Transform == null || !(_shapeGeometry.Transform is TranslateTransform))
            {
                _shapeGeometry.Transform = new TranslateTransform(offsetX, offsetY);
            }
            else
            {
                var tt = _shapeGeometry.Transform as TranslateTransform;
                tt.X = offsetX;
                tt.Y = offsetY;
            }

            _shapeGeometry = _shapeGeometry.GetOutlinedPathGeometry();
        }

        private void DoHitTest()
        {
            if (_landscape == null)
            {
                _landscape = ZTreeHelper.TryFindParent<ZInformationLandscape>(View);
            }

            if (_landscape == null)
                return;

            var landscapeTransform = _landscape.LandscapeTransform;

            var hitTestGeometry = _shapeGeometry.Clone();
            hitTestGeometry.Transform = landscapeTransform;

            IEnumerable<DependencyObject> result;
            if (Model.HitTestTypes != null)
            {
                result = ZHitTestHelper.GetElementsInGeometry(hitTestGeometry, _landscape, Model.HitTestTypes.ToArray());
            }
            else
            {
                result = ZHitTestHelper.GetElementsInGeometry(hitTestGeometry, _landscape);
            }

            var hitTestResult = result
                .OfType<ZComponent>()
                .Where(element => !element.GetType().IsAssignableFrom(typeof(ZPortalView))
                                && !element.GetType().IsAssignableFrom(typeof(ZPortalShapeView))
                                && !element.GetType().IsAssignableFrom(typeof(ShapeToShapeConnectionView)))
                .Select(element =>
                {
                    var vm = (IViewModel)element.DataContext;
                    return vm.Model;
                });

            Model.SelectedElements.Clear();
            foreach (var r in hitTestResult.ToArray())
            {
                Model.SelectedElements.Add(r);
            }
        }

        #endregion

        #region event handling

        private void OnShapeMoved()
        {
            RecalculateShapeGeometry();
            DoHitTest();
            ConnectionShape = _shapeGeometry;
        }

        #endregion

        #region commands

        protected override void OnElementDragged()
        {
            _shapeMoves.OnNext(new Unit());

            base.OnElementDragged();
        }

        protected override void UpdateConnectionShape()
        {
            //ConnectionShape = Model.ShapeGeometry;
        }

        protected override void OnElementAdded()
        {
            InitShape();

            base.OnElementAdded();
        }

        #endregion
    }
}
